﻿#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
using namespace std;

class Animal {
public:
	Animal() {
		a = 100;
	}
public:
	int a;
};

class Sheep : virtual public Animal {

};

class Camel : virtual public Animal {

};

class SheepCamel : public Sheep, public Camel {

};

/*
菱形继承：一个基类有两个子类，又有一个孙子类同时继承了这两个子类，这就是菱形继承。
菱形继承的二义性问题：
	Sheep中Animal继承了a成员，Camel也从Animal中继承了a成员，因此，SheepCamel中就有两份a了，有二义性问题。
解决菱形继承的二义性问题：
	使用虚继承，被继承类加上virtual关键字，class Sheep : virtual public Animal，class Camel : virtual public Animal 
虚继承的原理：
	1.编译器给子类添加了一个指针，指向类似于表的一个组织，该表记录了该指针距离变量的偏移量；
	2.当子类多继承两个父类，那么只有一份成员变量，然后有两个指针。 
*/
void test01() {
	SheepCamel sc;
	cout << sc.a << endl; // 不使用虚继承时会有二义性问题，报错 
}

// 通过偏移量访问基类中的成员变量 
void test02() {
	Sheep s;
	cout << s.a << endl; // 100

	// 1. &s; 取对象地址（即结构体地址），整个Sheep结构体包含两个字段：虚表指针vtptr和int a；
	// 2. (int*)&s;  强转为int指针，为了+1时步长为4字节；
	// 3. *(int*)&s;  获取vtptr的地址，即对象的前4个字节
	// 4. (int*)*(int*)&s;  将vtptr的地址转为int指针，即该指针现在指向的是vtptr第一个元素；
	// 5. (int*)*(int*)&s+1;  上面指针加1，指向vtptr第二个元素，虚表vtptr中第二个元素指的是父类对象；
	// 6. *((int*)*(int*)&s+1);  取出父类对象相对于子类对象首地址的偏移量；
	cout << "父类对象相对于子类对象首地址的偏移量：" << *((int*)*(int*)&s + 1) << endl;  // 4 

	// 1. &s;  对象首地址 
	// 2. (char*)&s;  步长改为1字节
	// 3. (char*)&s + *((int*)*(int*)&s+1);  加上上面计算出来的偏移量，即为父类对象的地址 
	// 4. (Animal*)((char*)&s + *((int*)*(int*)&s+1))  强转为Animal类型的指针 
	// 5. ((Animal*)((char*)&s + *((int*)*(int*)&s+1)))->a得到了父类Animal对象的指针了，即可获取到父类对象的成员a的值了
	cout << "a=" << ((Animal*)((char*)&s + *((int*)*(int*)&s + 1)))->a << endl;  // 100 
}

int main()
{

	test02(); 

	system("pause");
	return EXIT_SUCCESS;
}